package com.lwq.aop;

import com.alibaba.fastjson.JSON;
import com.lwq.enums.HttpMethod;
import com.lwq.model.SysLog;
import com.lwq.manager.AsyncFactory;
import com.lwq.manager.AsyncManager;
import com.lwq.service.TokenService;
import com.lwq.util.DateUtil;
import com.lwq.util.ServletUtils;
import com.lwq.util.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

/**
 * Description:
 * Author: LiuWenQing
 * Datetime: 2021/6/15
 */
@Aspect
@Component
public class LogAop {

    private static final Logger log = LoggerFactory.getLogger(LogAop.class);

    @Autowired
    private TokenService tokenService;

    @Pointcut("@annotation(com.lwq.annotation.Log)")
    public void logPointCut() { };

    @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult)
    {
        handleLog(joinPoint, null, jsonResult);
    }

    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e)
    {
        handleLog(joinPoint, e, null);
    }


    protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult){
        try{
            SysLog sysLog =new SysLog();
            sysLog.setStatus(SysLog.SUCCESS_STATUS);
            sysLog.setResponseData(JSON.toJSONString(jsonResult));
            sysLog.setPath(ServletUtils.getRequest().getRequestURI());
            if (e != null) {
                sysLog.setStatus(SysLog.FAILED_STATUS);
                sysLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
            }
            sysLog.setMethod(joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()");
            sysLog.setRequestMethod(ServletUtils.getRequest().getMethod());
            setRequestValue(joinPoint, sysLog);
            sysLog.setCreateDate(DateUtil.getNowDate());
            sysLog.setCreateId(tokenService.getUserId());
            AsyncManager.me().execute(AsyncFactory.recordOper(sysLog));
        }catch(Exception exception){
            log.error("异常信息:{}", exception.getMessage());
            exception.printStackTrace();
        }
    }


    private void setRequestValue(JoinPoint joinPoint, SysLog operLog) throws Exception {
        String requestMethod = operLog.getRequestMethod();
        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
            String params = argsArrayToString(joinPoint.getArgs());
            operLog.setRequestParam(StringUtils.substring(params, 0, 2000));
        } else {
            Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            operLog.setRequestParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
        }
    }

    private String argsArrayToString(Object[] paramsArray)
    {
        String params = "";
        if (paramsArray != null && paramsArray.length > 0)
        {
            for (int i = 0; i < paramsArray.length; i++)
            {
                if (StringUtils.isNotNull(paramsArray[i]) && !isFilterObject(paramsArray[i]))
                {
                    Object jsonObj = JSON.toJSON(paramsArray[i]);
                    params += jsonObj.toString() + " ";
                }
            }
        }
        return params.trim();
    }

    public boolean isFilterObject(final Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray())
        {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        }
        else if (Collection.class.isAssignableFrom(clazz))
        {
            Collection collection = (Collection) o;
            for (Iterator iter = collection.iterator(); iter.hasNext();)
            {
                return iter.next() instanceof MultipartFile;
            }
        }
        else if (Map.class.isAssignableFrom(clazz))
        {
            Map map = (Map) o;
            for (Iterator iter = map.entrySet().iterator(); iter.hasNext();)
            {
                Map.Entry entry = (Map.Entry) iter.next();
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                || o instanceof BindingResult;
    }
}
